﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using YAPL.Language.Core.Types;
using YAPL.Util;

namespace YAPL.Language.Ast {
	/// <summary>
	/// This class contains more convenient static methods for expression creation.
	/// Use it instead of invoking expression constructors directly.
	/// </summary>
	public static class Expressions {

		private static readonly ConstantExpression _undefined = new ConstantExpression(ExpressionType.Constant, typeof(YAPLObject), null);
		public static ProgramDefExpression Program(CodeBlockExpression programBody) {
			Require.NotNull(programBody);
			return new ProgramDefExpression(ExpressionType.ProgramDef, typeof(void), true, false, programBody);
		}

		public static ClassDefExpression ClassDef(string className, CodeBlockExpression classBody) {
			return new ClassDefExpression(ExpressionType.ClassDef, typeof(void), true, false, className, classBody);
		}
		
		public static CodeBlockExpression CodeBlock(IList<Expression> expressions) {
			Require.NotNull(expressions);
			return new CodeBlockExpression(ExpressionType.CodeBlock, typeof(void), true, false, new List<Expression>(expressions));
		}

		public static CodeBlockExpression EmptyCodeBlock() {
			return CodeBlock(new List<Expression>());
		}

		public static ReturnExpression ReturnStatement(Expression statement) {
			Require.NotNull(statement);
			return new ReturnExpression(ExpressionType.Return, statement.Type, true, false, statement);
		}

		public static FunctionDefExpression Function(string name, IList<ParameterExpression> parameters, CodeBlockExpression functionBody) {
			Require.NotNull(functionBody);
			return new FunctionDefExpression(ExpressionType.FunctionDef, typeof(YAPLObject), true, false, name, null,
				new List<ParameterExpression>(parameters), functionBody);
		}

		public static FunctionDefExpression Function(string name, Expression owner, IList<ParameterExpression> parameters, CodeBlockExpression functionBody) {
			Require.NotNull(functionBody);
			return new FunctionDefExpression(ExpressionType.FunctionDef, typeof(YAPLObject), true, false, name, owner,
				new List<ParameterExpression>(parameters), functionBody);
		}

		public static FunctionCallExpression FunctionCall(string methodName, IList<Expression> arguments) {
			return new FunctionCallExpression(ExpressionType.FunctionCall, typeof(YAPLObject), true, false, methodName,
				new List<Expression>(arguments), null);
		}

		public static FunctionCallExpression FunctionCall(string methodName, IList<Expression> arguments, Expression callingObject) {
			return new FunctionCallExpression(ExpressionType.FunctionCall, typeof(YAPLObject), true, false, methodName,
				new List<Expression>(arguments), callingObject);
		}

		public static ConstantExpression StringLiteral(string value) {
			return new ConstantExpression(ExpressionType.Constant, typeof(YAPLString), value);
		}

		public static ConstantExpression Integer(int value) {
			return new ConstantExpression(ExpressionType.Constant, typeof(YAPLInt), value);
		}

		public static ConstantExpression Float(double value) {
			return new ConstantExpression(ExpressionType.Constant, typeof(YAPLFloat), value);
		}

		public static ConstantExpression Boolean(bool value) {
			return new ConstantExpression(ExpressionType.Constant, typeof(YAPLBoolean), value);
		}

		public static ConstantExpression Constant(Type ctype, object value) {
			return new ConstantExpression(ExpressionType.Constant, ctype, value);
		}

		public static ConstantExpression Undefined() {
			return _undefined;
		}

		public static AssignmentExpression Assignment(Expression left, Expression right) {
			Require.NotNull(left);
			Require.NotNull(right);
			return new AssignmentExpression(ExpressionType.Assignment, left.Type, true, false, left, right);
		}

		public static VariableExpression Variable(string name) {
			return new VariableExpression(ExpressionType.Variable, typeof(YAPLObject), name);
		}

		public static InstanceVariableExpression InstanceVariable(string name) {
			return new InstanceVariableExpression(ExpressionType.InstanceVariable, typeof(YAPLObject), name);
		}

		public static ClassReferenceExpression ClassReference(string name) {
			return new ClassReferenceExpression(ExpressionType.ClassReference, typeof(YAPLObject), name);
		}

		public static ParameterExpression Parameter(VariableExpression variable, Expression defaultValue) {
			Require.NotNull(variable);
			Require.NotNull(defaultValue);
			return new ParameterExpression(ExpressionType.Parameter, typeof(void), variable, defaultValue);
		}

		public static BinaryExpression Binary(Expression left, Expression right, BinaryOperator op) {
			Require.NotNull(left);
			Require.NotNull(right);
			return new BinaryExpression(ExpressionType.Binary, typeof(YAPLObject), true, false, left, right, op);
		}

		public static UnaryExpression Unary(Expression operand, UnaryOperator op) {
			Require.NotNull(operand);
			return new UnaryExpression(ExpressionType.Unary, typeof (YAPLObject), true, false, operand, op);
		}

		public static ConditionalExpression Conditional(Expression condition, CodeBlockExpression thenBlock, CodeBlockExpression elseBlock) {
			return new ConditionalExpression(ExpressionType.Conditional, typeof(void), condition, thenBlock, elseBlock);
		}

		public static ConditionalExpression Conditional(Expression condition, CodeBlockExpression thenBlock) {
			return new ConditionalExpression(ExpressionType.Conditional, typeof(void), condition, thenBlock, null);
		}

		public static WhileExpression While(Expression condition, CodeBlockExpression body) {
			return new WhileExpression(ExpressionType.While, typeof(void), condition, body);
		}
	}
}
